# -*- coding: utf-8 -*-
import geatpy as ea  # 导入geatpy库


class soea_SGA_templet(ea.SoeaAlgorithm):
    """
    soea_SGA_templet : class - Simple GA Algorithm(最简单、最经典的遗传算法类).

    算法描述:
        本算法类实现的是最经典的单目标遗传算法。算法流程如下：
        1) 根据编码规则初始化N个个体的种群。
        2) 若满足停止条件则停止，否则继续执行。
        3) 对当前种群进行统计分析，比如记录其最优个体、平均适应度等等。
        4) 独立地从当前种群中选取N个母体。
        5) 独立地对这N个母体进行交叉操作。
        6) 独立地对这N个交叉后的个体进行变异，得到下一代种群。
        7) 回到第2步。

    """

    def __init__(self,
                 problem,
                 population,
                 MAXGEN=None,
                 MAXTIME=None,
                 MAXEVALS=None,
                 MAXSIZE=None,
                 logTras=None,
                 verbose=None,
                 outFunc=None,
                 drawing=None,
                 trappedValue=None,
                 maxTrappedCount=None,
                 dirName=None,
                 **kwargs):
        # 先调用父类构造方法
        super().__init__(problem,
                         population,
                         MAXGEN,
                         MAXTIME,
                         MAXEVALS,
                         MAXSIZE,
                         logTras,
                         verbose,
                         outFunc,
                         drawing,
                         trappedValue,
                         maxTrappedCount,
                         dirName)
        if population.ChromNum != 1:
            raise RuntimeError('传入的种群对象必须是单染色体的种群类型。')
        self.name = 'SGA'
        self.selFunc = 'rws'  # 轮盘赌选择算子
        if population.Encoding == 'P':
            self.recOper = ea.Xovpmx(XOVR=0.7)  # 生成部分匹配交叉算子对象
            self.mutOper = ea.Mutinv(Pm=0.01)  # 生成逆转变异算子对象
        else:
            self.recOper = ea.Xovdp(XOVR=0.7)  # 生成两点交叉算子对象
            if population.Encoding == 'BG':
                self.mutOper = ea.Mutbin(
                    Pm=None)  # 生成二进制变异算子对象，Pm设置为None时，具体数值取变异算子中Pm的默认值
            elif population.Encoding == 'RI':
                self.mutOper = ea.Mutbga(Pm=1 / self.problem.Dim,
                                         MutShrink=0.5,
                                         Gradient=20)  # 生成breeder GA变异算子对象
            else:
                raise RuntimeError('编码方式必须为'
                                   'BG'
                                   '、'
                                   'RI'
                                   '或'
                                   'P'
                                   '.')

    def run(self, prophetPop=None):  # prophetPop为先知种群（即包含先验知识的种群）
        # ==========================初始化配置===========================
        population = self.population
        NIND = population.sizes
        self.initialization()  # 初始化算法类的一些动态参数
        # ===========================准备进化============================
        population.initChrom(NIND)  # 初始化种群染色体矩阵
        # 插入先验知识（注意：这里不会对先知种群prophetPop的合法性进行检查）
        if prophetPop is not None:
            population = (prophetPop + population)[:NIND]  # 插入先知种群
        self.call_aimFunc(population)  # 计算种群的目标函数值
        population.FitnV = ea.scaling(population.ObjV,
                                      population.CV,
                                      self.problem.maxormins)  # 计算适应度
        # ===========================开始进化============================
        while not self.terminated(population):
            # 选择
            population = population[ea.selecting(self.selFunc,
                                                 population.FitnV,
                                                 NIND)]
            # 进行进化操作
            population.Chrom = self.recOper.do(population.Chrom)  # 重组
            population.Chrom = self.mutOper.do(population.Encoding,
                                               population.Chrom,
                                               population.Field)  # 变异
            self.call_aimFunc(population)  # 计算目标函数值
            population.FitnV = ea.scaling(population.ObjV,
                                          population.CV,
                                          self.problem.maxormins)  # 计算适应度
        return self.finishing(population)  # 调用finishing完成后续工作并返回结果
